package com.cl.jvppeteerdemo.util;

import com.microsoft.playwright.*;
import com.microsoft.playwright.options.WaitUntilState;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

@Slf4j
public class PlaywrightUtil {
    private static Playwright _playwright;
    private static volatile Browser _browser;
    private static BrowserContext _browserContext;
    private static final AtomicLong renderNum = new AtomicLong(0);
    private static final int restartNum = 100;
    private static final String savePath = "/opt/img";

    private PlaywrightUtil() {
    }

    public static BrowserContext getBrowserContext() {
        if (_browser == null || !_browser.isConnected()) {
            synchronized (PlaywrightUtil.class) {
                if (_browser == null || !_browser.isConnected()) {
                    init();
                }
            }
        }
        long get = renderNum.incrementAndGet();
        if (get % restartNum == 0) {
            log.info("渲染次数为 {} 到达 {} ", get, restartNum);
            restart();
        }
        return _browserContext;
    }

    private static void init() {
        long start = System.currentTimeMillis();
        close();
        try {
            _playwright = Playwright.create();
            BrowserType.LaunchOptions launchOptions = new BrowserType.LaunchOptions();
            launchOptions.setArgs(List.of(
                    "--disable-gpu",
                    "--disable-dev-shm-usage",
                    "--disable-setuid-sandbox",
                    "--no-first-run",
                    "--no-sandbox",
                    "--single-process",
                    "--disable-blink-features=AutomationControlled",
                    "--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
                    "--no-zygote"
            ));
            _browser = _playwright.chromium().launch(launchOptions.setHeadless(true));
            _browserContext = _browser.newContext();
            _browserContext.newPage();
            log.info("chromium 启动成功 用时：{}ms", System.currentTimeMillis() - start);
        } catch (Exception e) {
            log.error("chromium 启动失败", e);
            close();
        }
    }

    public static void close() {
        safeClose(_browser);
        safeClose(_playwright);
    }

    public static void restart() {
        close();
        getBrowserContext();
        log.info("chromium 重启成功");
    }

    public static void safeClose(AutoCloseable closeable) {
        try {
            if (closeable != null) {
                closeable.close();
            }
        } catch (Exception ignored) {
        }
    }

    public static byte[] screenshot(String path, String url) {
        return screenshot(path, url, null, null);
    }


    public static byte[] screenshot(String path, String url, String selector) {
        return screenshot(path, url, selector, null);
    }

    /**
     * 截图
     *
     * @param path     Paths.get(savePath,path)
     * @param url      网页地址
     * @param selector 选择器
     * @param css      样式
     * @return 截图地址
     */
    public static byte[] screenshot(String path, String url, String selector, String css) {
        long start = System.currentTimeMillis();
        byte[] screenshot;
        try (Page page = getBrowserContext().newPage()) {

            // 设置新页面 加载时间节点  等
            Page.NavigateOptions navigateOptions = new Page.NavigateOptions();
            //COMMIT -当收到网络响应并开始加载文档时，将操作视为已完成。
            //DOMCONTENTLOADED -考虑在触发domcontentloaded事件时完成操作。
            //LOAD -考虑在启动加载事件时完成操作。
            //NETWORKIDLE -**DISCOURAGED**考虑在至少500毫秒内没有网络连接时完成操作。不要使用此方法进行测试，而是依靠网络断言来评估准备情况。
            navigateOptions.setWaitUntil(WaitUntilState.LOAD);
            page.navigate(url, navigateOptions);
            if (StringUtils.hasText(css)) {
                Page.AddStyleTagOptions addStyleTagOptions = new Page.AddStyleTagOptions();
                addStyleTagOptions.setContent(css);
                page.addStyleTag(addStyleTagOptions);
            }

            if (StringUtils.hasText(selector)) {
                ElementHandle elementHandle = page.waitForSelector(selector);
                ElementHandle.ScreenshotOptions screenshotOptions = new ElementHandle.ScreenshotOptions();
                screenshotOptions.setPath(getPath(path));
                screenshot = elementHandle.screenshot(screenshotOptions);
            } else {
                Page.ScreenshotOptions screenshotOptions = new Page.ScreenshotOptions();
                screenshotOptions.setFullPage(true);
                screenshotOptions.setPath(getPath(path));
                screenshot = page.screenshot(screenshotOptions);
            }
            log.info("图片生成成功,耗时：{}ms url: {}", System.currentTimeMillis() - start, url);
        }
        return screenshot;
    }

    private static Path getPath(String path) {
        String[] split = path.split("/");
        ArrayList<String> list = new ArrayList<>();
        for (String str : split) {
            if (StringUtils.hasText(str)) {
                list.add(str);
            }
        }
        if (list.isEmpty()) {
            throw new RuntimeException("截图路径错误");
        }
        return Paths.get(savePath, list.toArray(new String[0]));
    }


}
